package generics_and_collections.queue;

/**
 * Created by Sam on 08/02/14.
 *
 * FIFO - only allowed removal (pop) from front and insertion (push) at rear.
 *
 * % remainder after division e.g. 10 % 3 = 1 (3 goes in to 10 3 times, leaving 1)
 */

public class MyQueue implements IQueue {

    public Object[] queue;
    public int maxSize;
    // just pointers - queue can shift and wrap around, both f and r can move independently.
    // both represent next empty position after add & remove
    public int front = 0;
    public int rear = 0;    // always refers to EMPTY location (next empty slot)
    public int size = 0; // combat problem of r & f pointing to same index when full and empty

    public MyQueue(int maxSize){
        // fixed size queue, so implement wrap around / circular queue
        this.maxSize = maxSize;
        queue = new Object[maxSize];
    }

    @Override
    // join rear of queue (queue grows from end), as we add to queue, end point grows.
    // rear moves up a position when we add
    public void add(Object obj) {
        if(!isFull()) {
            queue[rear] = obj;
            rear = (rear+1)%maxSize;
            size++;
            System.out.println("add " +obj.toString() + " next rear " + rear + " front "+front);
        } else {
            System.out.println("queue full");
        }
    }

    @Override
    // remove from front of queue (first/oldest item), front of queue is now next position.
    // front moves up a position when we remove
    public Object remove() {
        Object obj = queue[front];
        front = (front+1)%maxSize;
        size--;
        System.out.println("remove " + obj.toString() + " front "+front+ " rear " + rear);
        return obj;
    }

    @Override
    public boolean isEmpty() {
        // rear == front is true for empty and full, so:
        // we need 'counter' and to protect against adding when full
        return rear == front;
    }

    public boolean isFull(){
        return size == maxSize;
    }

    public int size() {
        return size;
        /*
        if(rear > front ) { // normal (not wrapped yet) can just count rear (minus anything removed from front)
            return rear - front;
        } else {    // rear wrapped and front must have moved
            return (maxSize-(front+rear));  // rear is less, but front more, so add both
        }
        */
    }

    public String toString(){
        StringBuffer sb = new StringBuffer();

        for(int i = 0; i < queue.length; i++) {
            if(queue[i] != null) {
                sb.append(i + " " + queue[i].toString());
            } else {
                sb.append(i + " null");
            }

            sb.append('\n');
        }
        return sb.toString();
    }
}
